package com.da.common.utils;


import com.da.common.entity.SnowflakeProperties;
import com.da.common.setvice.IdGenerator;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: hyacinth
 * @CreateTime: 2025-01-11
 * @Version: 1.0
 * 雪花算法生成id
 */
@Component
public class SnowflakeIdWorker {
    private static long workerId;
    private static long dataCenterId;
    private static long epoch;
    private  long workerIdBits;
    private  long dataCenterIdBits;
    private  long sequenceBits;
    private  long maxWorkerId;
    private  long maxDataCenterId;
    private static long sequenceMask;
    private static long workerIdShift;
    private static long dataCenterIdShift;
    private static long timestampShift;
    private static long lastTimestamp = -1L;
    private static long sequence = 0L;

    private static final ReentrantLock lock = new ReentrantLock();


    @Resource
    private SnowflakeProperties properties;
    @Resource
    private IdGenerator idGenerator;
    @PostConstruct
    public void init() {
        this.epoch = properties.getEpoch();
        this.workerIdBits = properties.getWorkerIdBits();
        this.dataCenterIdBits = properties.getDataCenterIdBits();
        this.sequenceBits = properties.getSequenceBits();
        this.workerId=idGenerator.getWorkerId();
        this.dataCenterId=idGenerator.getDataCenterId();
        if (workerId > ( -1L ^ (-1L << this.workerIdBits)) || workerId < 0) {
            throw new IllegalArgumentException(String.format("Worker ID 不能大于 %d 或小于 0", -1L ^ (-1L << this.workerIdBits)));
        }
        if (dataCenterId > ( -1L ^ (-1L << this.dataCenterIdBits)) || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("Data Center ID 不能大于 %d 或小于 0", -1L ^ (-1L << this.dataCenterIdBits)));
        }

        this.maxWorkerId = -1L ^ (-1L << this.workerIdBits);
        this.maxDataCenterId = -1L ^ (-1L << this.dataCenterIdBits);
        this.sequenceMask = -1L ^ (-1L << this.sequenceBits);
        this.workerIdShift = this.sequenceBits;
        this.dataCenterIdShift = this.sequenceBits + this.workerIdBits;
        this.timestampShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits;

    }

    public static long generateId() {
        lock.lock();
        try {
            long timestamp = System.currentTimeMillis();
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(String.format("时钟回拨，上次时间是 %d，当前时间是 %d", lastTimestamp, timestamp));
            }
            if (timestamp == lastTimestamp) {
                sequence = (sequence + 1) & sequenceMask;
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0;
            }
            lastTimestamp = timestamp;
            return ((timestamp - epoch) << timestampShift) |
                    (dataCenterId << dataCenterIdShift) |
                    (workerId << workerIdShift) |
                    sequence;
        } finally {
            lock.unlock();
        }
    }


    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}
